package search.service.analysis;

import org.elasticsearch.index.query.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import search.config.AnalyzerNames;
import search.config.SearchParameters;
import common.dao.elastic.ElasticSearchDao;
import common.dao.gremlin.TitanGraphDao;
import search.restapi.SearchRestAPI;
import search.service.graph.EntityExpansionService;

import java.util.List;

/**
 * Created by julianzliu on 2016/7/3.
 */
public class QueryExpansion {
    final static Logger logger = LoggerFactory.getLogger(SearchRestAPI.class);

    public static BoolQueryBuilder queryWithSimpleStringQuery(String q,String field,String minimum,String analyzer){
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        SimpleQueryStringBuilder string_query = QueryBuilders.
                simpleQueryStringQuery(q)
                .field(field);
        String minimum_str = SearchParameters.MinimumMatch.ori_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.default_query_analyzer;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        query.should(
                string_query
                        .analyzer(analyzer_str)
                        .minimumShouldMatch(minimum_str)
        );
        return query;
    }

    public static BoolQueryBuilder queryWithMatchQuery(String text,String field,String minimum,String analyzer){
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        MatchQueryBuilder match_query = QueryBuilders
                .matchQuery(field,text);
        String minimum_str = SearchParameters.MinimumMatch.ori_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.default_query_analyzer;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        match_query
                .analyzer(analyzer_str)
                .minimumShouldMatch(minimum_str);
        query.should(match_query);
        return query;
    }


    public static BoolQueryBuilder queryWithMultiMatchQuery(String text,String[] fields,String minimum,String analyzer){
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        MultiMatchQueryBuilder match_query = QueryBuilders
                .multiMatchQuery(text,fields);
        String minimum_str = SearchParameters.MinimumMatch.ori_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.default_query_analyzer;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        match_query
                .analyzer(analyzer_str)
                .minimumShouldMatch(minimum_str);
        query.should(match_query);
        return query;
    }

    public static BoolQueryBuilder queryWithFieldsMatchQuery(String text, String analyzer){
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        /*
        1) title
         */
        String[] title_fields = {"Metadata.Program.SearchTitle", "Metadata.Sequence.SearchTitle",
                "Metadata.Scene.Title.SceneTitle","Metadata.Shot.Title.ShotTitle"};
        BoolQueryBuilder titel_query = queryWithMultiMatchQuery(text, title_fields, "50%", analyzer  );
        query.should( titel_query.boost(SearchParameters.Boosting.title) );
        return query;
    }


    /*************************************************************************************/
    public static BoolQueryBuilder queryExpansionWithSynoWithMatchQuery(ElasticSearchDao esDao, String text,String field,String minimum,String analyzer){
        logger.info("[queryExpansionWithSynoWithMatchQuery] text = {}", text);
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        String minimum_str = SearchParameters.MinimumMatch.ori_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.default_query_analyzer;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        List<String> tokenList = AnalysisService.getTokenTermList(esDao, text, analyzer_str);
        for(String token : tokenList){
            // NOTE: For Chinese, use this trick
            // if(token.length() <= 1) continue;
            String expam_analyzer = AnalyzerNames.ik_smart_syno;
            List<String> tokenSynoList = AnalysisService.getTokenTermList(esDao, token, expam_analyzer);
            BoolQueryBuilder syno_query = QueryBuilders.boolQuery();
            for(String syno : tokenSynoList ){
                // NOTE: For Chinese, Use This Trick to get semantic related result
                //if(syno.length() < token.length())
                if(token.length() > 1 && syno.length() <= 1)
                    continue;
                String smart_analyzer = AnalyzerNames.ik_smart;
                MatchQueryBuilder base = QueryBuilders.matchQuery(field , syno);
                base.analyzer(smart_analyzer).minimumShouldMatch("100%");
                syno_query.should(base);
            }
            // 一个 token 扩展的同义词集合中，至少匹配一个
            syno_query.minimumShouldMatch("1");
            float token_boost = AnalyzerNames.ik_smart_syno_boost;
            if( tokenSynoList.size() > 0){
                token_boost = 1.0f / tokenSynoList.size();
            }
            float boost = token_boost;//boostList.get(i).floatValue();
            syno_query.boost(boost);
            //MatchQueryBuilder base = QueryBuilders.matchQuery(field , token);
            //base.analyzer(expam_analyzer);
            //base.boost(boost);
            query.should(syno_query);
        }
        // 原始查询得到的多个token中，至少有多少个 token_query 要匹配
        query.minimumShouldMatch(minimum_str);
        return query;
    }

    public static BoolQueryBuilder queryExpansionWithDictForMatchQuery(ElasticSearchDao esDao, String text,String field,
                                                                       String minimum, String analyzer) throws Exception{
        /*
        拆分成多次词语的子查询，
        每个原始词语变为一个子查询
         */
        BoolQueryBuilder multi = QueryBuilders.boolQuery();
        String minimum_str = SearchParameters.MinimumMatch.exp_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.ik_smart;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        logger.debug("In {} : minimum = {} , analyzer = {}", "QueryExpansion.queryExpansionWithDictForMultiMatchQuery", minimum_str, analyzer_str);
        List<String> tokenList = AnalysisService.getTokenTermList(esDao, text, analyzer_str);
        for(String token : tokenList){
            /*
            对于原始查询的分词结果中的每个词语，
            使用语义词典的多种扩展来匹配这个词语
             */
            BoolQueryBuilder token_query = QueryBuilders.boolQuery();
            List<String> analyzerList = AnalyzerNames.getExpansionAnalyzers();
            List<Double> boostList = AnalyzerNames.getExpasionBoosting();
            for(int i = 0; i < analyzerList.size();i++){
                String expam_analyzer = analyzerList.get(i);
                float boost = boostList.get(i).floatValue();
                MatchQueryBuilder base = QueryBuilders.matchQuery(field , token);
                base.analyzer(expam_analyzer);
                base.boost(boost);
                token_query.should(base);
            }
            multi.should(token_query);
        }
        // 最少有多少个 token_query 要成功
        multi.minimumShouldMatch(minimum_str);
        return multi;
    }


    public static BoolQueryBuilder queryExpansionWithDictForMultiMatchQuery(ElasticSearchDao esDao, String text,String[] fields,
                                                                            String minimum, String analyzer) throws Exception{
        BoolQueryBuilder multi = QueryBuilders.boolQuery();
        String minimum_str = SearchParameters.MinimumMatch.exp_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.ik_smart;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        logger.debug("In {} : minimum = {} , analyzer = {}", "QueryExpansion.queryExpansionWithDictForMultiMatchQuery", minimum_str, analyzer_str);
        List<String> tokenList = AnalysisService.getTokenTermList(esDao, text, analyzer_str);
        for(String token : tokenList){
            BoolQueryBuilder token_query = QueryBuilders.boolQuery();
            List<String> analyzerList = AnalyzerNames.getExpansionAnalyzers();
            List<Double> boostList = AnalyzerNames.getExpasionBoosting();
            for(int i = 0; i < analyzerList.size();i++){
                String expan_analyzer = analyzerList.get(i);
                float boost = boostList.get(i).floatValue();
                MultiMatchQueryBuilder base =
                        QueryBuilders.multiMatchQuery( token, fields);
                base
                        .analyzer(expan_analyzer)
                        .boost(boost);
                token_query.should(base);
            }
            multi.should(token_query);
        }
        multi.minimumShouldMatch(minimum_str);
        return multi;
    }


    public static BoolQueryBuilder queryExpansionWithGraph(ElasticSearchDao esDao, TitanGraphDao titanDao,
                                                           String text, String field,
                                                           String minimum, String analyzer) throws Exception {
        BoolQueryBuilder multi = QueryBuilders.boolQuery();
        String minimum_str = SearchParameters.MinimumMatch.exp_minimum;
        if(!minimum.isEmpty()) minimum_str = minimum;
        String analyzer_str = AnalyzerNames.ik_smart;
        if(!analyzer.isEmpty()) analyzer_str = AnalyzerNames.get_analyzer(analyzer);
        logger.info("In {} : minimum = {} , analyzer = {}", "QueryExpansion.queryExpansionWithGraph", minimum_str, analyzer_str);
        List<String> tokenList = AnalysisService.getTokenTermList(esDao, text, analyzer_str);
        EntityExpansionService service = new EntityExpansionService( esDao);//, titanDao);
        for(String token : tokenList){
            List<String> expansions = service.entityExpansionByToken(token);
            BoolQueryBuilder token_query = QueryBuilders.boolQuery();
            for(String expan_token: expansions ){
                MatchQueryBuilder base = QueryBuilders.matchQuery(field , expan_token);
                base.analyzer(AnalyzerNames.ik_smart);
                token_query.should( base );
            }

            multi.should( token_query );
        }
        multi.minimumShouldMatch( minimum_str );
        return multi;
    }



}
